package My

import (
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"time"
)

type Partition struct {
	Schema         string
	Table          string
	SqlExecTimeOut time.Duration
}

func (my Partition) PartitionType(s global.PartitionInput) (p global.PartitionType, err error) {
	var (
		event       = "[PartitionTableType]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	strSql := fmt.Sprintf("select TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,PARTITION_METHOD AS  `partitionType` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND PARTITION_METHOD IS NOT NULL", schema, table)
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.Map); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	if err = global.MapToStructJson(f1.(map[string]any), &p); err != nil {
		return
	}
	return
}
func (my Partition) SubPartitionType(s global.PartitionInput) (p global.SubPartitionType, err error) {
	var (
		event       = "[PartitionTableType]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	strSql := fmt.Sprintf("select TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,SUBPARTITION_METHOD AS  `partitionType` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND SUBPARTITION_METHOD IS NOT NULL", schema, table)
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.Map); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	if err = global.MapToStructJson(f1.(map[string]any), &p); err != nil {
		return
	}
	return
}
func (my Partition) PartitionColumn(s global.PartitionInput) (p []global.PartitionColumn, err error) {
	var (
		event       = "[PartitionRules]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	strSql := fmt.Sprintf("SELECT  DISTINCT TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,PARTITION_EXPRESSION AS `partitionColumnName` FROM INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND PARTITION_EXPRESSION IS NOT NULL", schema, table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	for _, v := range f1.([]map[string]any) {
		if v1, ok := v["partitionColumnName"]; ok {
			var v2 string
			if n := strings.Index(v1.(string), "(`"); n != -1 {
				v2 = v1.(string)[n+2:]
			}
			if n := strings.Index(v2, "`)"); n != -1 {
				v2 = v2[:n]
			}
			strSql = fmt.Sprintf("SELECT DATA_TYPE AS `partitionColumnType` FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='%v' AND TABLE_NAME='%v' AND COLUMN_NAME='%v'", schema, table, v2)
			if execTimeout, f1, err = active.SqlQuery(strSql, ETL.Map); err != nil {
				vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
				WLog.Error(vlog)
				return
			}
			v["partitionColumnName"] = v2
			for k3, v3 := range f1.(map[string]any) {
				v[k3] = v3
			}
			s1 := global.PartitionColumn{}
			if err = global.MapToStructJson(v, &s1); err != nil {
				return
			}
			p = append(p, s1)
		}
	}
	return
}
func (my Partition) SubPartitionColumn(s global.PartitionInput) (p []global.SubPartitionColumn, err error) {
	var (
		event       = "[SubPartitionColumn]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	strSql := fmt.Sprintf("SELECT  DISTINCT TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,SUBPARTITION_EXPRESSION AS `subPartitionColumnName` FROM INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v'", schema, table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	for _, v := range f1.([]map[string]any) {
		if v1, ok := v["subPartitionColumnName"]; ok {
			var v2 string
			if n := strings.Index(v1.(string), "(`"); n != -1 {
				v2 = v1.(string)[n+2:]
			}
			if n := strings.Index(v2, "`)"); n != -1 {
				v2 = v2[:n]
			}
			strSql = fmt.Sprintf("SELECT DATA_TYPE AS `subPartitionColumnType` FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='%v' AND TABLE_NAME='%v' AND COLUMN_NAME='%v' AND SUBPARTITION_EXPRESSION IS NOT NULL", schema, table, v2)
			if execTimeout, f1, err = active.SqlQuery(strSql, ETL.Map); err != nil {
				vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
				WLog.Error(vlog)
				return
			}
			v["partitionColumnName"] = v2
			for k3, v3 := range f1.(map[string]any) {
				v[k3] = v3
			}
			s1 := global.SubPartitionColumn{}
			if err = global.MapToStructJson(v, &s1); err != nil {
				return
			}
			p = append(p, s1)
		}
	}
	return
}
func (my Partition) PartitionName(s global.PartitionInput) (p []global.PartitionName, err error) {
	var (
		event       = "[PartitionTableType]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	strSql := fmt.Sprintf("select TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,PARTITION_NAME AS  `partitionName`,PARTITION_ORDINAL_POSITION AS `partitionSeq` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND PARTITION_NAME IS NOT NULL", schema, table)
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	if f1 != nil {
		for _, v := range f1.([]map[string]any) {
			var v1 = make(map[string]any)
			for k2, v2 := range v {
				if strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(v2)), "godror.Number") {
					v1[k2] = fmt.Sprintf("%v", v2)
				} else {
					v1[k2] = v2
				}
			}
			s1 := global.PartitionName{}
			if err = global.MapToStructJson(v1, &s1); err != nil {
				return
			}
			p = append(p, s1)
		}
	}
	return
}
func (my Partition) SubPartitionName(s global.PartitionInput) (p []global.SubPartitionName, err error) {
	var (
		event       = "[PartitionTableType]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	strSql := fmt.Sprintf("select TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`,PARTITION_NAME AS  `partitionName`,SUBPARTITION_NAME AS `subPartitionName`,SUBPARTITION_ORDINAL_POSITION AS `subPartitionSeq` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND SUBPARTITION_NAME IS NOT NULL", schema, table)
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	if f1 != nil {
		for _, v := range f1.([]map[string]any) {
			var v1 = make(map[string]any)
			for k2, v2 := range v {
				if strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(v2)), "godror.Number") {
					v1[k2] = fmt.Sprintf("%v", v2)
				} else {
					v1[k2] = v2
				}
			}
			s1 := global.SubPartitionName{}
			if err = global.MapToStructJson(v1, &s1); err != nil {
				return
			}
			p = append(p, s1)
		}
	}
	return
}

func (my Partition) PartitionRules(s global.PartitionInput) (p []global.PartitionRules, err error) {
	var (
		event       = "[PartitionRules]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table

	strSql := fmt.Sprintf("select DISTINCT TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`, PARTITION_NAME AS `partitionName` ,CONCAT(PARTITION_EXPRESSION,'||',PARTITION_DESCRIPTION) AS  `partitionRules`,PARTITION_ORDINAL_POSITION AS `partitionSeq` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND PARTITION_NAME IS NOT NULL", schema, table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	for _, v := range f1.([]map[string]any) {
		s1 := global.PartitionRules{}
		if err = global.MapToStructJson(v, &s1); err != nil {
			return
		}
		p = append(p, s1)
	}
	return
}
func (my Partition) SubPartitionRules(s global.PartitionInput) (p []global.SubPartitionRules, err error) {
	var (
		event       = "[PartitionRules]"
		f1          any
		execTimeout int64
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	strSql := fmt.Sprintf("select TABLE_SCHEMA AS `schema`,TABLE_NAME AS `table`, PARTITION_NAME AS `partitionName` ,SUBPARTITION_NAME AS  `subPartitionName`,SUBPARTITION_EXPRESSION AS `subPartitionRules`,SUBPARTITION_ORDINAL_POSITION AS `subPartitionSeq` from INFORMATION_SCHEMA.`PARTITIONS` where table_SCHEMA ='%v' and table_name='%v' AND SUBPARTITION_NAME IS NOT NULL", schema, table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	for _, v := range f1.([]map[string]any) {
		s1 := global.SubPartitionRules{}
		if err = global.MapToStructJson(v, &s1); err != nil {
			return
		}
		p = append(p, s1)
	}
	return
}

func (my Partition) Configuration(s global.PartitionInput) (tpc global.PartitionInfo, err error) {
	var (
		event = "[Configuration]"
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	//获取表的分区类型
	if tpc.PartitionType, err = my.PartitionType(s); err != nil {
		return
	}
	if _, err = my.SubPartitionType(s); err != nil {
		return
	}
	if tpc.PartitionRules, err = my.PartitionRules(s); err != nil {
		return
	}
	if tpc.SubPartitionRules, err = my.SubPartitionRules(s); err != nil {
		return
	}
	if tpc.PartitionColumn, err = my.PartitionColumn(s); err != nil {
		return
	}
	if tpc.SubPartitionColumn, err = my.SubPartitionColumn(s); err != nil {
		return
	}
	return
}

func (my Partition) Sum(s global.PartitionInput) (global.PartitionSummary, error) {
	var (
		event         = "[IfTablePartitionSum]"
		execTimeout   int64
		f1            any
		partitionSum  int64
		err           error
		partitionType string
	)
	p := global.PartitionSummary{}
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Start checking whether table %v.%v exists.", s.LogSeq, callFuncInfo, event, my.Schema, my.Table)
	WLog.Debug(vlog)
	schema := s.Schema
	table := s.Table
	strSql := fmt.Sprintf("SELECT (select distinct concat(PARTITION_METHOD,',',IFNULL(SUBPARTITION_METHOD,''))  from partitions where table_schema='%v' and table_name='%v' and PARTITION_METHOD is not null) as partitionType,count(1) AS `sum` FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA = '%v' AND TABLE_NAME = '%v' and PARTITION_METHOD is not null", schema, table, schema, table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.Db, StopTime: my.SqlExecTimeOut}
	if execTimeout, f1, err = active.SqlQuery(strSql, ETL.Map); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, strSql, err)
		WLog.Error(vlog)
		return p, err
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, strSql, execTimeout)
	WLog.Debug(vlog)
	if f1 != nil {
		f2 := f1.(map[string]interface{})
		if v, ok := f2["sum"]; ok {
			if d, err1 := strconv.ParseInt(fmt.Sprintf("%v", v), 10, 64); err1 == nil {
				if d > 0 {
					partitionSum = d
				}
			} else {
				vlog = fmt.Sprintf("(%d) %v %v Table %v.%v row count conversion failed. data is %v, error is %v", s.LogSeq, callFuncInfo, event, my.Schema, my.Table, v, err)
				WLog.Warn(vlog)
			}
		}
		if v, ok := f2["partitionType"]; ok {
			partitionType = fmt.Sprintf("%v", v)
		}
	}
	WLog.Warn(fmt.Sprintf("(%d) %v %v Check whether table a exists or not is completed. result is {%v}", s.LogSeq, callFuncInfo, event, "table not is Exist!"))
	p.Sum = partitionSum
	p.PartitionType = partitionType
	return p, nil
}
